﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using System.Data.SqlClient;
using System.Threading.Tasks;
using System.Data;
using Insight.Database;
using System.Data.Common;

#pragma warning disable 0649

namespace Insight.Tests
{
    /// <summary>
    /// Tests the various methods of opening connections.
    /// </summary>
    [TestFixture]
    public class OpenTests : BaseTest
    {
        #region Setup
        protected SqlConnectionStringBuilder _connectionStringBuilder;

        [OneTimeSetUp]
        public virtual void SetUpFixture()
        {
            // open the test connection
            _connectionStringBuilder = new SqlConnectionStringBuilder(ConnectionString);
        }
        #endregion

        [Test]
        public void Open()
        {
            TestConnection(() => _connectionStringBuilder.Open());
            TestConnection(() => _connectionStringBuilder.OpenAsync().Result);
            TestConnection(() => _connectionStringBuilder.OpenAs<ITest1>());
            TestConnection(() => _connectionStringBuilder.OpenAsAsync<ITest1>().Result);
            TestConnection(() => _connectionStringBuilder.OpenWithTransaction(), true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransaction(IsolationLevel.ReadCommitted), true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAsync().Result, true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAsync(IsolationLevel.ReadCommitted).Result, true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAs<ITest1>(), true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAs<ITest1>(IsolationLevel.ReadCommitted), true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAsAsync<ITest1>().Result, true);
            TestConnection(() => _connectionStringBuilder.OpenWithTransactionAsAsync<ITest1>(IsolationLevel.ReadCommitted).Result, true);
        }

        [Test]
        public void ConnectionsShouldBeReusableForQuerySql()
        {
            var connection = Connection();

            var results = connection.QuerySql("SELECT 1");
            var results2 = connection.QuerySql("SELECT 1");
        }

        [Test]
        public void OpenWithTransactionsShouldBeReusableForQuerySql()
        {
            var connection = Connection();

            using (var c = connection.OpenWithTransaction())
            {
                var results = c.QuerySql("SELECT 1");
            }

            using (var c = connection.OpenWithTransaction())
            {
                var results2 = c.QuerySql("SELECT 1");
            }
        }

        [Test]
        public void OpenWithTransactionsShouldHandleClosedConnection()
        {
            var connection = Connection();

            using (var c = connection.OpenWithTransaction())
            {
                var results = c.QuerySql("SELECT 1");
                c.Close();
            }
        }

        [Test]
        public void FailedEnumerationShouldAutoClose()
        {
            var connection = Connection();

            try
            {
                string sql = "SELECT p=1 SELECT p=2";
                using (var reader = connection.GetReaderSql(sql, Parameters.Empty))
                {
                    foreach (var i in reader.AsEnumerable<int>())
                        throw new Exception();
                }
            }
            catch { }

            ClassicAssert.AreEqual(ConnectionState.Closed, connection.State);
        }

#if !NO_AMBIENT_TRANSACTIONS
		[Test]
        public void TestAmbientTransaction()
        {
            using (var t = new System.Transactions.TransactionScope())
            {
                Connection().ExecuteSql("CREATE PROC AmbientTransactionShouldRollback AS SELECT 1");
            }

			Assert.Throws<InvalidOperationException>(()=> Connection().Execute("AmbientTransactionShouldRollback"));
		}
#endif

        private void TestConnection<T>(Func<T> opener, bool checkTransaction = false) where T : IDisposable, IDbConnection
        {
            using (var c = opener())
            {
                AssertConnection(c);
                ClassicAssert.IsInstanceOf<T>(c);

                if (checkTransaction)
                {
                    DbConnectionWrapper wrapper = (DbConnectionWrapper)(object)c;
                    ClassicAssert.IsNotNull(wrapper.InnerTransaction);
                }
            }
        }

        private void AssertConnection(IDbConnection c)
        {
            ClassicAssert.IsNotNull(c);
            ClassicAssert.IsInstanceOf<IDbConnection>(c);
            ClassicAssert.AreEqual(ConnectionState.Open, c.State);
        }
    }
}
